#ifndef _MultiPtr_H_
#define _MultiPtr_H_ 1
#include<iostream>
#include<type_traits>
namespace Skip{
    /**
     * @brief no need function
    */
    bool if_delete(std::size_t &n){
        if(--n==0)return true;
        else return false;
    }
}
namespace Locale{    
    /**
     *@brief Reference Counter Class
    */
    class UseCount{
    protected:
        std::size_t *p;//critical part of counter
        /**
         * @brief private function to avoid changing the value easily
        */
        UseCount& operator=(const UseCount&);
    public:
        /**
         * @brief change the counter to provided one
        */
        bool reattach(const UseCount&);
        UseCount()try:p(new std::size_t(1)){}
            catch(std::bad_alloc&balloc){
                std::cerr<<balloc.what()<<std::endl;
                p=nullptr;}
        UseCount(const UseCount&);
        virtual ~UseCount();
        /**
         * @brief return the count of current counter
        */
        std::size_t count()const noexcept{return *p;}
        /**
         * @brief make the counter be unique
         * @return if only return false
         * else return true
         * also YOU CAN CHANGE IT if want
        */
        virtual bool makeOnly();
        /**
         * @brief Check the counter is unique or not
        */
        bool only()const{return *p==1;}
    };
    /**
     * @brief [Simple shared_ptr]
     * template pointer manage class with reference counter
     * maybe can't easily manage the original array
    */
    template<typename t>
    class MultiPtr{
    public:
        /**
         * @brief make it possible to convert
        */
        template<typename _type>
        friend class MultiPtr;
        MultiPtr():data(nullptr){}
        MultiPtr(MultiPtr const&p):data(p.data),u(p.u){}
        MultiPtr(t*pd):data(pd){}
        /**
         * @brief construct function adapting to variable type which is convertible
         * Can't convert from integral to floating point and in converse
         * However it's stiff
        */
        template<typename _i,
            typename=
                typename std::enable_if<std::is_convertible<_i,t>::value
                    &&(std::is_integral<_i>::value ^ std::is_floating_point< t>::value)
                    &&(std::is_integral< t>::value ^ std::is_floating_point<_i>::value)>::type>
        MultiPtr(MultiPtr<_i>&ip):data(reinterpret_cast<t*>(ip.data)),u(ip.u){}
        MultiPtr(t &&p)try:data(new t(p)){}
            catch(std::bad_alloc&balloc){
                std::cerr<<balloc.what()<<std::endl;
                data=nullptr;}
        MultiPtr(t const&p)try:data(new t(p)){}
            catch(std::bad_alloc&balloc){
                std::cerr<<balloc.what()<<std::endl;
                data=nullptr;}
        virtual ~MultiPtr();
        t& operator*()const{return *data;}
        t* operator->()const{return data;}
        MultiPtr& operator=(MultiPtr const&);
        /**
         * @brief copy operator adapting to variable type
         * Can't convert from integral to floating point and in converse
        */
        template<typename _t,
            typename= 
                typename std::enable_if<std::is_convertible<_t,t>::value
                &&(std::is_integral<_t>::value ^ std::is_floating_point< t>::value)
                &&(std::is_integral< t>::value ^ std::is_floating_point<_t>::value)
                >::type>
        MultiPtr& operator=(MultiPtr<_t>*);
        template<typename _t,
            typename= 
                typename std::enable_if<std::is_convertible<_t,t>::value
                &&(std::is_integral<_t>::value ^ std::is_floating_point< t>::value)
                &&(std::is_integral< t>::value ^ std::is_floating_point<_t>::value)
                >::type>
        MultiPtr& operator=(_t*);
        /**
         * @brief the equal operator adapting to variable type
         * convertion limit in float and integral
        */
        template<typename _t,
            typename= 
                typename std::enable_if<std::is_convertible<_t,t>::value
                &&(std::is_integral<_t>::value ^ std::is_floating_point< t>::value)
                &&(std::is_integral< t>::value ^ std::is_floating_point<_t>::value)
                >::type>
        bool operator==(MultiPtr<_t> const&p)const noexcept{return data==p.data;}
        bool is_unique()const noexcept{return u.only();}
        /**
         * @brief true if the counter is unique
         * false if needed to makeOnly
        */
        bool unique();
        /**
         * @brief return the pointer managed
        */
        t*   get()const noexcept{return data;}
        std::size_t count()const noexcept{return u.count();}
        operator bool()const noexcept{
            return data!=nullptr;
        }
        /**
         * @brief output the addr
         */
        template<typename _type>
        friend std::ostream& operator<<(std::ostream&os,MultiPtr<_type>_ptr);
    protected:
        t* data;
        UseCount u;
    };
    /**
     * @brief simulation of the make_ptr() to make the code look better
     * @param v small type for building
    */
    template<typename value_type>
        MultiPtr<typename std::remove_reference<value_type>::type> make_ptr(value_type &&v){
            using type=typename std::remove_reference<value_type>::type;
            MultiPtr<type>ptr;
            try{
                ptr = new type(v);
            }catch(std::bad_alloc&ba){
                std::cout<<ba.what()<<std::endl;
            }catch(...){
                throw;
            }return ptr;
        }

    //-------------implement part---------------//
    template<typename _type>
    std::ostream& operator<<(std::ostream&os,MultiPtr<_type>_ptr){
        os<<_ptr.data;
        return os;
    }
    bool UseCount::makeOnly(){
        if(this->only())return false;
        --*p;
        try{
            p=new std::size_t(1);
        }catch(std::bad_alloc&balloc){
            std::cerr<<balloc.what()<<std::endl;
            p=nullptr;}
        return true;
    }
    bool UseCount::reattach(const UseCount&uc){
        ++*uc.p;
        if(--*p==0){
            delete p;
            p=uc.p;
            return true;
        }p=uc.p;
        return false;
    }
    UseCount::UseCount(const UseCount&uc):p(uc.p){++*p;}
    UseCount::~UseCount(){if(Skip::if_delete(*p))delete p;}
    UseCount& UseCount::operator=(const UseCount&uc){
        ++*uc.p;
        if(Skip::if_delete(*p))delete p;
        p=uc.p;
        return *this;
    }
    template<typename _t>
    MultiPtr<_t>::~MultiPtr(){
        if(u.only()){
            delete data;
        }
    }
    template<typename t>
        template<typename _t,
            typename>
        MultiPtr<t>& MultiPtr<t>::operator=(MultiPtr<_t>*newp){
            if(!u.makeOnly())
                delete data;
            data=static_cast<MultiPtr<t>>(newp);
            return *this;
        }
    template<typename t>
        template<typename _t,
            typename>
        MultiPtr<t>& MultiPtr<t>::operator=(_t* p){
            if(!u.makeOnly())
                delete data;
            data=p;
            /* [deprecated]try{
                data=new t(*p);
            }catch(std::bad_alloc&balloc){
                std::cerr<<balloc.what()<<std::endl;
                data=nullptr;} */
            return *this;
        }
    template<typename t>
    MultiPtr<t>& MultiPtr<t>::operator=(MultiPtr const&cp){
        if(is_unique()&&this!=&cp)
            delete data;
        data=cp.data;
        u.reattach(cp.u);
        return *this;
    }
    template<typename t>
    bool MultiPtr<t>::unique(){
        if(!u.makeOnly())
            return true;
        t& value=*data;
        try{
            data=new t(value);
        }catch(std::bad_alloc&balloc){
            std::cerr<<balloc.what()<<std::endl;
            data=nullptr;}
        return false;
    }
}
#endif